/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 *
 * ***************************************************************************/

using System.Collections.Generic;
using Microsoft.Scripting.Utils;

namespace Microsoft.Scripting.Ast {
    partial class Walker {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
        public void WalkNode(Expression node) {
            if (node == null) {
                return;
            }

            switch (node.NodeType) {
                case ExpressionType.Add:
                case ExpressionType.AddChecked:
                case ExpressionType.And:
                case ExpressionType.AndAlso:
                case ExpressionType.ArrayIndex:
                case ExpressionType.Divide:
                case ExpressionType.Equal:
                case ExpressionType.ExclusiveOr:
                case ExpressionType.GreaterThan:
                case ExpressionType.GreaterThanOrEqual:
                case ExpressionType.LeftShift:
                case ExpressionType.LessThan:
                case ExpressionType.LessThanOrEqual:
                case ExpressionType.Modulo:
                case ExpressionType.Multiply:
                case ExpressionType.MultiplyChecked:
                case ExpressionType.NotEqual:
                case ExpressionType.Or:
                case ExpressionType.OrElse:
                case ExpressionType.RightShift:
                case ExpressionType.Subtract:
                case ExpressionType.SubtractChecked:
                case ExpressionType.Power:
                case ExpressionType.Coalesce:
                    DefaultWalk((BinaryExpression)node);
                    break;
                case ExpressionType.Call:
                    DefaultWalk((MethodCallExpression)node);
                    break;
                case ExpressionType.Conditional:
                    DefaultWalk((ConditionalExpression)node);
                    break;
                case ExpressionType.Constant:
                    DefaultWalk((ConstantExpression)node);
                    break;
                case ExpressionType.Convert:
                case ExpressionType.ConvertChecked:
                case ExpressionType.Negate:
                case ExpressionType.NegateChecked:
                case ExpressionType.Not:
                case ExpressionType.OnesComplement:
                case ExpressionType.UnaryPlus:
                case ExpressionType.TypeAs:
                case ExpressionType.ArrayLength:
                case ExpressionType.Quote:
                    DefaultWalk((UnaryExpression)node);
                    break;
                case ExpressionType.New:
                    DefaultWalk((NewExpression)node);
                    break;
                case ExpressionType.TypeIs:
                    DefaultWalk((TypeBinaryExpression)node);
                    break;
                case ExpressionType.ActionExpression:
                    DefaultWalk((ActionExpression)node);
                    break;
                case ExpressionType.Block:
                    DefaultWalk((Block)node);
                    break;
                case ExpressionType.Assign:
                    DefaultWalk((AssignmentExpression)node);
                    break;
                case ExpressionType.Variable:
                    DefaultWalk((VariableExpression)node);
                    break;
                case ExpressionType.Parameter:
                    DefaultWalk((ParameterExpression)node);
                    break;
                case ExpressionType.BreakStatement:
                    DefaultWalk((BreakStatement)node);
                    break;
                case ExpressionType.Lambda:
                case ExpressionType.Generator:
                    DefaultWalk((LambdaExpression)node);
                    break;
                case ExpressionType.LocalScope:
                    DefaultWalk((LocalScopeExpression)node);
                    break;
                case ExpressionType.ContinueStatement:
                    DefaultWalk((ContinueStatement)node);
                    break;
                case ExpressionType.Delete:
                    DefaultWalk((DeleteExpression)node);
                    break;
                case ExpressionType.DoStatement:
                    DefaultWalk((DoStatement)node);
                    break;
                case ExpressionType.EmptyStatement:
                    DefaultWalk((EmptyStatement)node);
                    break;
                case ExpressionType.LabeledStatement:
                    DefaultWalk((LabeledStatement)node);
                    break;
                case ExpressionType.LoopStatement:
                    DefaultWalk((LoopStatement)node);
                    break;
                case ExpressionType.MemberAccess:
                    DefaultWalk((MemberExpression)node);
                    break;
                case ExpressionType.NewArrayInit:
                case ExpressionType.NewArrayBounds:
                    DefaultWalk((NewArrayExpression)node);
                    break;
                case ExpressionType.ReturnStatement:
                    DefaultWalk((ReturnStatement)node);
                    break;
                case ExpressionType.Scope:
                    DefaultWalk((ScopeExpression)node);
                    break;
                case ExpressionType.SwitchStatement:
                    DefaultWalk((SwitchStatement)node);
                    break;
                case ExpressionType.ThrowStatement:
                    DefaultWalk((ThrowStatement)node);
                    break;
                case ExpressionType.TryStatement:
                    DefaultWalk((TryStatement)node);
                    break;
                case ExpressionType.YieldStatement:
                    DefaultWalk((YieldStatement)node);
                    break;
                case ExpressionType.Invoke:
                    DefaultWalk((InvocationExpression)node);
                    break;
                case ExpressionType.Extension:
                    WalkExtension(node);
                    PostWalkExtension(node);
                    break;
                default:
                    throw Assert.Unreachable;
            }
        }

        // Extension
        protected virtual bool WalkExtension(Expression node) { return true; }
        protected virtual void PostWalkExtension(Expression node) { }

        public void WalkNode(CatchBlock node) {
            DefaultWalk(node);
        }

        public void WalkNode(LambdaExpression node) {
            DefaultWalk(node);
        }

        public void WalkNode(IfStatementTest node) {
            DefaultWalk(node);
        }

        public void WalkNode(SwitchCase node) {
            DefaultWalk(node);
        }

        // ActionExpression
        private void DefaultWalk(ActionExpression node) {
            if (Walk(node)) {
                foreach (Expression ex in node.Arguments) {
                    WalkNode(ex);
                }
            }
            PostWalk(node);
        }

        // BinaryExpression
        private void DefaultWalk(BinaryExpression node) {
            if (Walk(node)) {
                WalkNode(node.Left);
                WalkNode(node.Right);
                WalkNode((Expression)node.Conversion);
            }
            PostWalk(node);
        }

        // AssignmentExpression
        private void DefaultWalk(AssignmentExpression node) {
            if (Walk(node)) {
                WalkNode(node.Expression);
                WalkNode(node.Value);
            }
            PostWalk(node);
        }

        // VariableExpression
        private void DefaultWalk(VariableExpression node) {
            Walk(node);
            PostWalk(node);
        }

        // ParameterExpression 
        private void DefaultWalk(ParameterExpression node) {
            Walk(node);
            PostWalk(node);
        }

        // ConditionalExpression
        private void DefaultWalk(ConditionalExpression node) {
            if (Walk(node)) {
                WalkNode(node.Test);
                WalkNode(node.IfTrue);
                WalkNode(node.IfFalse);
            }
            PostWalk(node);
        }

        // ConstantExpression
        private void DefaultWalk(ConstantExpression node) {
            Walk(node);
            PostWalk(node);
        }

        // LocalScopeExpression
        private void DefaultWalk(LocalScopeExpression node) {
            Walk(node);
            PostWalk(node);
        }

        // MemberExpression
        private void DefaultWalk(MemberExpression node) {
            if (Walk(node)) {
                WalkNode(node.Expression);
            }
            PostWalk(node);
        }

        // MethodCallExpression
        private void DefaultWalk(MethodCallExpression node) {
            if (Walk(node)) {
                WalkNode(node.Object);
                IList<Expression> args = node.Arguments;
                if (args != null) {
                    foreach (Expression e in args) {
                        WalkNode(e);
                    }
                }
            }
            PostWalk(node);
        }

        // InvocationExpression
        private void DefaultWalk(InvocationExpression node) {
            if (Walk(node)) {
                WalkNode(node.Expression);
                IList<Expression> args = node.Arguments;
                if (args != null) {
                    foreach (Expression e in args) {
                        WalkNode(e);
                    }
                }
            }
            PostWalk(node);
        }

        // NewArrayExpression
        private void DefaultWalk(NewArrayExpression node) {
            if (Walk(node)) {
                foreach (Expression expr in node.Expressions) {
                    WalkNode(expr);
                }
            }
            PostWalk(node);
        }

        // NewExpression
        private void DefaultWalk(NewExpression node) {
            if (Walk(node)) {
                IList<Expression> args = node.Arguments;
                if (args != null) {
                    foreach (Expression e in args) {
                        WalkNode(e);
                    }
                }
            }
            PostWalk(node);
        }

        // TypeBinaryExpression
        private void DefaultWalk(TypeBinaryExpression node) {
            if (Walk(node)) {
                WalkNode(node.Expression);
            }
            PostWalk(node);
        }

        // UnaryExpression
        private void DefaultWalk(UnaryExpression node) {
            if (Walk(node)) {
                WalkNode(node.Operand);
            }
            PostWalk(node);
        }

        // Block
        private void DefaultWalk(Block node) {
            if (Walk(node)) {
                foreach (Expression expr in node.Expressions) {
                    WalkNode(expr);
                }
            }
            PostWalk(node);
        }

        // BreakStatement
        private void DefaultWalk(BreakStatement node) {
            Walk(node);
            PostWalk(node);
        }

        // ContinueStatement
        private void DefaultWalk(ContinueStatement node) {
            Walk(node);
            PostWalk(node);
        }

        // DeleteExpression
        private void DefaultWalk(DeleteExpression node) {
            if (Walk(node)) {
                WalkNode(node.Expression);
            }
            PostWalk(node);
        }

        // DoStatement
        private void DefaultWalk(DoStatement node) {
            if (Walk(node)) {
                WalkNode(node.Body);
                WalkNode(node.Test);
            }
            PostWalk(node);
        }

        // EmptyStatement
        private void DefaultWalk(EmptyStatement node) {
            Walk(node);
            PostWalk(node);
        }

        // LabeledStatement
        private void DefaultWalk(LabeledStatement node) {
            if (Walk(node)) {
                WalkNode(node.Statement);
            }
            PostWalk(node);
        }

        // LoopStatement
        private void DefaultWalk(LoopStatement node) {
            if (Walk(node)) {
                WalkNode(node.Test);
                WalkNode(node.Increment);
                WalkNode(node.Body);
                WalkNode(node.ElseStatement);
            }
            PostWalk(node);
        }

        // ReturnStatement
        private void DefaultWalk(ReturnStatement node) {
            if (Walk(node)) {
                WalkNode(node.Expression);
            }
            PostWalk(node);
        }

        // ScopeExpression
        private void DefaultWalk(ScopeExpression node) {
            if (Walk(node)) {
                WalkNode(node.Body);
            }
            PostWalk(node);
        }

        // SwitchStatement
        private void DefaultWalk(SwitchStatement node) {
            if (Walk(node)) {
                WalkNode(node.TestValue);
                foreach (SwitchCase sc in node.Cases) {
                    WalkNode(sc.Body);
                }
            }
            PostWalk(node);
        }

        // ThrowStatement
        private void DefaultWalk(ThrowStatement node) {
            if (Walk(node)) {
                WalkNode(node.Value);
            }
            PostWalk(node);
        }

        // TryStatement
        private void DefaultWalk(TryStatement node) {
            if (Walk(node)) {
                WalkNode(node.Body);
                if (node.Handlers != null) {
                    foreach (CatchBlock handler in node.Handlers) {
                        if (handler.Filter != null) {
                            WalkNode(handler.Filter);
                        }
                        WalkNode(handler);
                    }
                }
                WalkNode(node.FinallyStatement);
                WalkNode(node.FaultStatement);
            }
            PostWalk(node);
        }

        // YieldStatement
        private void DefaultWalk(YieldStatement node) {
            if (Walk(node)) {
                WalkNode(node.Expression);
            }
            PostWalk(node);
        }

        // CatchBlock
        private void DefaultWalk(CatchBlock node) {
            if (Walk(node)) {
                WalkNode(node.Variable);
                WalkNode(node.Body);
            }
            PostWalk(node);
        }

        // LambdaExpression
        private void DefaultWalk(LambdaExpression node) {
            if (Walk(node)) {
                WalkNode(node.Body);
            }
            PostWalk(node);
        }

        // IfStatementTest
        private void DefaultWalk(IfStatementTest node) {
            if (Walk(node)) {
                WalkNode(node.Test);
                WalkNode(node.Body);
            }
            PostWalk(node);
        }

        // SwitchCase
        private void DefaultWalk(SwitchCase node) {
            if (Walk(node)) {
                WalkNode(node.Body);
            }
            PostWalk(node);
        }
    }
}
